package org.springblade.modules.repair.controller;

import cn.hutool.core.convert.Convert;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.log.annotation.ApiLog;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.repair.entity.PlanHistoryImage;
import org.springblade.modules.repair.service.PlanHistoryImageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriUtils;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

/**
 * @Date 14:41 2024/8/26
 * @Description {
 * oss 回调接口
 * }
 * @Author lsay
 **/
@Slf4j
@AllArgsConstructor
@RestController
@RequestMapping("/oss")
public class OssCallbackController {

	private final PlanHistoryImageService planHistoryImageService;

	@Autowired
	private AsyncTaskExecutor asyncTaskExecutor;

//	@ApiLog("OSS图片上传回调")
	@PostMapping("/callback")
	public ResponseEntity<String> handleOssCallback(@RequestBody String callbackBody, HttpServletRequest request) {

		log.info("==================================== { OSS_CALLBACK_START } =========================================");

		List<Object> beforeReqArgs = new ArrayList<>();
		StringBuilder beforeReqLog = new StringBuilder();
		Enumeration<String> headers = request.getHeaderNames();
		while (headers.hasMoreElements()) {
			String headerName = headers.nextElement();
			String headerValue = request.getHeader(headerName);
			beforeReqLog.append("===Headers===  {}: {}\n");
			beforeReqArgs.add(headerName);
			beforeReqArgs.add(headerValue);
		}
		beforeReqLog.append("===RequestBody===  RequestBody: ").append(decodeURIComponent(callbackBody)).append("\n");
		log.info(beforeReqLog.toString(),beforeReqArgs.toArray());

//		验证回调请求的合法性
//		boolean isValidCallback = verifyOssCallbackRequest(request);
//		if (!isValidCallback) {
//			log.info("==================================== { OSS_CALLBACK_ERROR } =========================================");
//			return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("{\"Status\":\"verdify not ok\"}");
//		}

		JSONObject callbackJson = JSONObject.parseObject(decodeURIComponent(callbackBody).substring(0,decodeURIComponent((callbackBody)).length() - 1));

		// 解析回调body
		if (Func.isEmpty(callbackJson.getString("url"))) {
			log.info("==================================== { OSS_CALLBACK_ERROR : url is empty ! } =========================================");
			return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("{\"Status\":\"verdify not ok\"}");
		}
		if (Func.isEmpty(callbackJson.getString("model_id"))) {
			log.info("==================================== { OSS_CALLBACK_ERROR : model_id is empty ! } =========================================");
			return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("{\"Status\":\"verdify not ok\"}");
		}

		// 异步处理业务逻辑
		asyncTaskExecutor.execute(() -> processCallbackData(callbackBody));


		log.info("==================================== { OSS_CALLBACK_END } =========================================");
		// 返回处理结果给OSS
		return ResponseEntity.ok("{\"Status\":\"OK\"}");
	}

	@Retryable(
		value = {Exception.class}, // 重试的异常类型
		maxAttempts = 3,          // 最大重试次数
		backoff = @Backoff(delay = 1000, multiplier = 2) // 重试延迟，每次延迟时间*2
	)
	private void processCallbackData(String callbackBody) {
		try {
			JSONObject callbackJson = JSONObject.parseObject(decodeURIComponent(callbackBody).substring(0, decodeURIComponent((callbackBody)).length() - 1));


			PlanHistoryImage image = new PlanHistoryImage();
			String result = callbackJson.getString("result");
			if(Func.isNotEmpty(result) && !"undefined".equals(result)){
				image.setResult(Func.isNotEmpty(result) ? Integer.valueOf(result) : null);
				String planId = callbackJson.getString("plan_id");
				image.setPlanId(Func.isNotEmpty(planId) ? Long.valueOf(planId) : null);
				String templateId = callbackJson.getString("template_id");
				image.setTemplateId(Func.isNotEmpty(templateId) ? Long.valueOf(templateId) : null);
				String templateContentId = callbackJson.getString("template_content_id");
				image.setTemplateContentId(Func.isNotEmpty(templateContentId) ? Long.valueOf(templateContentId) : null);
				String signId = callbackJson.getString("sign_id");
				image.setSignId(Func.isNotEmpty(signId) ? Long.valueOf(signId) : null);
				String imgFrom = callbackJson.getString("img_from");
				image.setImgFrom(Func.isNotEmpty(imgFrom) ? Integer.valueOf(imgFrom) : null);
				String createUser = callbackJson.getString("create_user");
				image.setCreateUser(Func.isNotEmpty(createUser) ? Long.valueOf(createUser) : null);
				image.setUpdateUser(Func.isNotEmpty(createUser) ? Long.valueOf(createUser) : null);
				String templateName = callbackJson.getString("template_name");
				image.setTemplateName(Convert.toStr(templateName, null));
				String locationTypeId = callbackJson.getString("location_type_id");
				image.setLocationTypeId(Convert.toStr(locationTypeId, null));
				String locationTypeName = callbackJson.getString("location_type_name");
				image.setLocationTypeName(Convert.toStr(locationTypeName, null));
				String templateContentName = callbackJson.getString("template_content_name");
				image.setTemplateContentName(Convert.toStr(templateContentName, null));
				String watermark = callbackJson.getString("watermark");
				image.setWatermark(Convert.toStr(watermark, null));
				String modelResult = callbackJson.getString("model_result");
				image.setModelResult(Convert.toStr(modelResult, null));
				String modelId = callbackJson.getString("model_id");
				image.setModelId(Convert.toStr(modelId, null));
				String tenantId = callbackJson.getString("tenant_id");
				image.setTenantId(Convert.toStr(tenantId, null));
				String url = callbackJson.getString("url");
				image.setUrl(Convert.toStr(url, null));
				String source = callbackJson.getString("source");
				image.setSource(Func.isNotEmpty(source) ? Integer.valueOf(source) : null);
				if(callbackJson.containsKey("business_result")){
					Integer businessResult = callbackJson.getInteger("business_result");
					image.setBusinessResult(businessResult);
				}
				planHistoryImageService.save(image);
			}

		} catch (Exception e) {
			log.error("处理OSS回调异常，准备重试", e);
			throw e; // 抛出异常以触发重试
		}
	}

	private boolean verifyOssCallbackRequest(HttpServletRequest request) {
		// 验证逻辑，确保请求来自OSS
		// 具体实现可以参考阿里云文档
		return true; // 这里简化处理，实际使用时需要真正的验证逻辑
	}

	public static void main(String[] args) {
		String str = "%7B%22with%22%3A1440%2C%22height%22%3A1843%2C%22plan_id%22%3A%22%22%2C%22location_type_id%22%3A%22%22%2C%22location_type_name%22%3A%22%22%2C%22template_content_id%22%3A%22%22%2C%22template_content_name%22%3A%22%22%2C%22img_from%22%3A%22%22%2C%22result%22%3A1%2C%22watermark%22%3A%22%7B%5C%22createTime%5C%22%3A%5C%222024-10-24+15%3A54%3A37%5C%22%2C%5C%22buildingName%5C%22%3A%5C%22%E4%B8%AD%E5%B1%B1%E6%B8%AF%E6%96%B0%E8%BE%BE%E8%8A%B1%E5%9B%AD%E4%BA%8C%E6%9C%9F%5C%22%2C%5C%22elevatorName%5C%22%3A%5C%229%E6%A0%8BK-32045%5C%22%2C%5C%22latitude%5C%22%3A22.557132975260416%2C%5C%22longitude%5C%22%3A113.32445638020833%2C%5C%22address%5C%22%3A%5C%22%E4%B8%AD%E5%B1%B1%E5%B8%82%E5%8D%93%E6%A2%85%E5%B0%BC%E7%94%B5%E6%A2%AF%E5%B7%A5%E4%B8%9A%E5%9B%AD%E8%A5%BF%E5%8D%97%28%E7%9B%88%E5%BA%B7%E8%A1%97%E5%8C%97%29%5C%22%7D%22%2C%22model_result%22%3A%22%5B%7B%5C%22key%5C%22%3A%5C%22headDetermine%5C%22%2C%5C%22minRatio%5C%22%3A%5C%220.7%5C%22%2C%5C%22passRatio%5C%22%3A%5C%220.8%5C%22%2C%5C%22determine%5C%22%3A0%2C%5C%22reason%5C%22%3A%5C%22%E6%9C%AA%E6%88%B4%E5%AE%89%E5%85%A8%E5%B8%BD%5C%22%2C%5C%22bboxInfo%5C%22%3A%7B%5C%22x%5C%22%3A%5C%22402.50%5C%22%2C%5C%22y%5C%22%3A%5C%22617.86%5C%22%2C%5C%22width%5C%22%3A%5C%22172.78%5C%22%2C%5C%22height%5C%22%3A%5C%22126.71%5C%22%7D%7D%2C%7B%5C%22key%5C%22%3A%5C%22footDetermine%5C%22%2C%5C%22minRatio%5C%22%3A%5C%220.7%5C%22%2C%5C%22passRatio%5C%22%3A%5C%220.8%5C%22%2C%5C%22determine%5C%22%3A0%2C%5C%22reason%5C%22%3A%5C%22%E6%9C%AA%E7%A9%BF%E6%88%B4%E5%8A%B3%E4%BF%9D%E9%9E%8B%5C%22%2C%5C%22bboxInfo%5C%22%3A%7B%5C%22x%5C%22%3A%5C%22345.80%5C%22%2C%5C%22y%5C%22%3A%5C%221614.10%5C%22%2C%5C%22width%5C%22%3A%5C%22126.71%5C%22%2C%5C%22height%5C%22%3A%5C%22138.22%5C%22%7D%7D%2C%7B%5C%22key%5C%22%3A%5C%22fenceDetermine%5C%22%2C%5C%22minRatio%5C%22%3A%5C%220.7%5C%22%2C%5C%22passRatio%5C%22%3A%5C%220.8%5C%22%2C%5C%22determine%5C%22%3A0%2C%5C%22reason%5C%22%3A%5C%22%E6%9C%AA%E6%91%86%E6%94%BE%E5%9B%B4%E6%A0%8F%5C%22%2C%5C%22bboxInfo%5C%22%3A%7B%5C%22x%5C%22%3A%5C%22538.18%5C%22%2C%5C%22y%5C%22%3A%5C%221597.94%5C%22%2C%5C%22width%5C%22%3A%5C%22126.71%5C%22%2C%5C%22height%5C%22%3A%5C%22120.95%5C%22%7D%7D%5D%22%2C%22model_id%22%3A%2204521863-0472-44be-b159-f3eeb2920916%22%2C%22tenant_id%22%3A%22592800%22%2C%22create_user%22%3A%221460777096537227266%22%7D=";
		System.err.println(JSONObject.parseObject(decodeURIComponent(str).substring(0, decodeURIComponent((str)).length() - 1)));
	}

	/**
	 * Description:
	 *
	 * @param encodedURI
	 * @return
	 * @see
	 */
	public static String decodeURIComponent(String encodedURI) {
		char actualChar;

		StringBuffer buffer = new StringBuffer();

		int bytePattern, sumb = 0;

		for (int i = 0, more = -1; i < encodedURI.length(); i++) {
			actualChar = encodedURI.charAt(i);

			switch (actualChar) {
				case '%': {
					actualChar = encodedURI.charAt(++i);
					int hb = (Character.isDigit(actualChar) ? actualChar - '0' : 10 + Character.toLowerCase(actualChar) - 'a') & 0xF;
					actualChar = encodedURI.charAt(++i);
					int lb = (Character.isDigit(actualChar) ? actualChar - '0' : 10 + Character.toLowerCase(actualChar) - 'a') & 0xF;
					bytePattern = (hb << 4) | lb;
					break;
				}
				case '+': {
					bytePattern = ' ';
					break;
				}
				default: {
					bytePattern = actualChar;
				}
			}

			if ((bytePattern & 0xc0) == 0x80) { // 10xxxxxx
				sumb = (sumb << 6) | (bytePattern & 0x3f);
				if (--more == 0) buffer.append((char) sumb);
			} else if ((bytePattern & 0x80) == 0x00) { // 0xxxxxxx
				buffer.append((char) bytePattern);
			} else if ((bytePattern & 0xe0) == 0xc0) { // 110xxxxx
				sumb = bytePattern & 0x1f;
				more = 1;
			} else if ((bytePattern & 0xf0) == 0xe0) { // 1110xxxx
				sumb = bytePattern & 0x0f;
				more = 2;
			} else if ((bytePattern & 0xf8) == 0xf0) { // 11110xxx
				sumb = bytePattern & 0x07;
				more = 3;
			} else if ((bytePattern & 0xfc) == 0xf8) { // 111110xx
				sumb = bytePattern & 0x03;
				more = 4;
			} else { // 1111110x
				sumb = bytePattern & 0x01;
				more = 5;
			}
		}
		return buffer.toString();
	}

}
